require 'test/unit'

require 'engine/logic'
require 'models/character'

class NilLogic < Logic
end
class EmptyLogic < Logic
    invoked_by
    needs_data
    optionally
end

class TestLogic < Logic
    invoked_by :a,:b,:c
    needs_data :x,:y,:z
    optionally :xx,:yy
    saveable
    stackable :replaceable
    applicable_to Character
    applied_message "Woot!"
    removed_message "Boo, Hiss"

    def do_logic; true end
end

class CharacterDatabase
    def add(c)
        @instances.add(c)
    end
end

class LogicTest < Test::Unit::TestCase
    def setup
        @entity = Character.new
        @entity.oid = 1
        CharacterDatabase.instance.add(@entity)
        @test = TestLogic.new(@entity)

        @nil = NilLogic.new #(@entity)
        @empty = EmptyLogic.new #(@entity)
    end
    def test_owner
        @test.owner = @entity
        assert_not_nil(@test.instance_variable_get("@props")[:owner_oid])
        assert_not_nil(@test.instance_variable_get("@props")[:owner_type])
        assert_equal(@entity,@test.owner)

    end
    def test_invoked_by
        assert(! @nil.respond_to?(:invokers))
        assert_equal([],@empty.invokers) 
        assert_equal([:a,:b,:c], @test.invokers)
        assert( ! @test.invokers.include?(:d))
    end
    def test_needs_data
        assert(! @nil.respond_to?(:needed_data))
        assert_equal([],@empty.needed_data) 
        assert_equal([:x,:y,:z], @test.needed_data)
        assert_respond_to(@test, :x)
        assert_respond_to(@test, :y)
        assert_respond_to(@test, :z)
    end
    def test_optionally
        assert(! @nil.respond_to?(:optional_data))
        assert_equal([], @empty.optional_data)
        assert_equal([:xx,:yy], @test.optional_data)
        assert_respond_to(@test, :xx)
        assert_respond_to(@test, :yy)
    end
    def test_saveable
        assert(! @empty.respond_to?(:save))
        assert(! @empty.respond_to?(:save?))
        assert(@test.save)
        assert(@test.save?)
    end
    def test_stackable
        assert_equal(:exclusive,@nil.stacking_rule)
        assert_equal(:exclusive,@empty.stacking_rule)
        assert_equal(:replaceable,@test.stacking_rule)
    end
    def test_applicable_to
        assert_raises(RuntimeError) {
            e = Entity.new
            e.oid = 1
            TestLogic.new(e)
        }
    end
    def test_messages
        assert(! @empty.respond_to?(:applied))
        assert(! @empty.respond_to?(:removed))

        assert_respond_to(@test,:applied)
        assert_respond_to(@test,:removed)
        assert_equal("Woot!",@test.applied)
        assert_equal("Boo, Hiss",@test.removed)
    end

    def test_needed_data
        require 'engine/action'
        # @test needs x,y,x
        assert_equal([:x,:y,:z],@test.needed_data)
        assert(@test.invokers.include?(:a))

        a = Action.new(:a,@entity,{:x => 1,
                       :y => 2, :z => 3})
        assert_nothing_thrown{@test.do_action(a)}

        b = Action.new(:a,@entity,{})
        assert_raises(RuntimeError){@test.do_action(b)}

        assert_equal(1,@test.x)
        assert_equal(2,@test.y)
        assert_equal(3,@test.z)
    end
end
